package com.apigateway.proxy;

import apigateway.serviceRegister.ServiceNode;
import com.apigateway.init.ServiceInit;
import com.apigateway.stateCount.StateCountUtil;
import com.apigateway.util.SpringContextUtils;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.*;
import io.netty.util.CharsetUtil;
import org.apache.http.NameValuePair;
import org.apache.http.ParseException;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ssm.serviceRegister.UriParam;

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.*;

import static io.netty.buffer.Unpooled.copiedBuffer;

/**
 * Created by 小泽玛丽罗 on 2017/2/28.
 */
public class NettyServerHandler extends SimpleChannelInboundHandler<FullHttpRequest> {

    private static Logger logger= LoggerFactory.getLogger(NettyProxy.class);
    private HttpRequest request;
    private String route;
    private static final  String UUID="uuid";
    public NettyServerHandler(){
        super();
    }
    private StateCountUtil stateCountUtil=
            (StateCountUtil) SpringContextUtils.getObject("stateCountUtil");
    public NettyServerHandler(String route){
        super();
        this.route=route;
    }

    HashMap<String, HashMap<String,List<ServiceNode>>> atomServices=ServiceInit.getAtomServices();
    HashMap<String, List<ServiceNode>> combinationServices=ServiceInit.getCombinationServices();
    @Override
    public void channelRead0(ChannelHandlerContext ctx, FullHttpRequest msg) throws Exception {
              //2
        try {
            long start=System.currentTimeMillis();
//        ArrayList<ServiceNode> serviceIPs= ServiceInit.getServiceIps();
            long proxyStartTime=0;
            long proxyEndTime=0;
            long end=0;
            String result=null;
            URI proxyUri=null;
            UriParam uriParam=null;
            ServiceNode serviceNode=null;
            if (msg instanceof HttpRequest) {
                request = (HttpRequest) msg;
                String uri = request.uri();
                System.out.println("Uri:" + uri);
                CloseableHttpClient httpClient= HttpClients.createDefault();
                uriParam=getUri(uri);
                serviceNode=routeServiceNode(uriParam);
                request.headers().get("Host");
                generateUuid(uriParam);
                proxyUri=makeUri(uriParam,serviceNode);
                if(proxyUri!=null){
                    HttpGet httpGet=new HttpGet(proxyUri);
                    httpGet.setHeader("Connection","close");
                    proxyStartTime=System.currentTimeMillis();
                    CloseableHttpResponse httpResponse=httpClient.execute(httpGet);
                    proxyEndTime=System.currentTimeMillis()-proxyStartTime;
                    result= EntityUtils.toString(httpResponse.getEntity());
                    httpResponse.close();
                    logger.info("proxy request "+uri+" to "+proxyUri.toString());
                }else{
                    result="no services is avaliable";
                    logger.warn("zk has no service name "+uri);
                }
                ByteBuf content=copiedBuffer(result, CharsetUtil.UTF_8);
                FullHttpResponse response=new DefaultFullHttpResponse(HttpVersion.HTTP_1_0,
                        HttpResponseStatus.OK
                        ,content);
                response.headers().set(HttpHeaderNames.CONTENT_LENGTH,content.readableBytes());
                if(proxyUri!=null){
                    response.headers().set(HttpHeaderNames.CONTENT_TYPE,HttpHeaderValues.APPLICATION_JSON);
                }else{
                    response.headers().set(HttpHeaderNames.CONTENT_TYPE,"text/plain; charset=UTF-8");
                }
                /*if (msg.headers().get(HttpHeaderNames.CONNECTION).equals(HttpHeaderValues.KEEP_ALIVE)) {
                    response.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.CLOSE);
                }*/
                response.headers().set(HttpHeaderNames.CONNECTION,HttpHeaderValues.CLOSE);
                ctx.write(response);
                logger.info("proxy request "+uri+" success");
                end=System.currentTimeMillis()-start;
                stateCountUtil.stateCount(start,proxyEndTime
                        ,uriParam.getParams().get("uuid"),serviceNode);
                httpClient.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ParseException e) {
            e.printStackTrace();
        }
    }



    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        ctx.writeAndFlush(Unpooled.EMPTY_BUFFER)//4
                .addListener(ChannelFutureListener.CLOSE);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx,
                                Throwable cause) {
        cause.printStackTrace();                //5
        ctx.close();                            //6
    }


    /**
     * 路由并获得ServiceNode m没有返回null
     * @param uriParam
     * @return
     */
    private ServiceNode routeServiceNode(UriParam uriParam){
        ServiceNode serviceNode=null;
        if(uriParam!=null&&checkType(uriParam)){
            List<ServiceNode> serviceNodes=null;
            HashMap<String,List<ServiceNode>> serviceNodesMap=null;
            if(uriParam.isAtom("type")){//原子服务
                serviceNodesMap=atomServices.get(uriParam.getParams().get("location"));
                if(serviceNodesMap==null)
                    return null;
                else{
                    String serviceCategory=null;
                    if(uriParam.getParams().containsKey("serviceCategory")
                            &&uriParam.getParams().get("serviceCategory")!=null){
                        serviceCategory=uriParam.getParams().get("serviceCategory");
                        serviceNodes=serviceNodesMap.get(serviceCategory);
                        }else {
                        serviceCategory=uriParam.getUri().replace("/","");
                        serviceNodes=serviceNodesMap.get(serviceCategory);
                    }
                    serviceNodes=serviceNodesMap.get(serviceCategory);
                    serviceNode=route(serviceNodes);
                }
            }else{//TODO 组合服务
                if(uriParam.getParams().containsKey("serviceCategory")){
                    serviceNodes=combinationServices
                            .get(uriParam.getParams().get("serviceCategory"));
                    serviceNode=route(serviceNodes);
                }
            }
        }
        return serviceNode;
    }
    /**
     * 构建uri，如果路由结果ServiceNode 是null 则返回结果也是null
     * @param uriParam
     * @param serviceNode
     * @return
     */
    private URI makeUri(UriParam uriParam,ServiceNode serviceNode){
        URI uri=null;
        if(serviceNode!=null&&uriParam!=null){
            List<NameValuePair> nameValuePairs=new ArrayList<>();
            Iterator iterator=uriParam.getParams().entrySet().iterator();
            while(iterator.hasNext()){
                Map.Entry entry= (Map.Entry) iterator.next();
                NameValuePair nameValuePair=new BasicNameValuePair(entry.getKey().toString()
                        ,(String) entry.getValue());
                nameValuePairs.add(nameValuePair);
            }
            if(!uriParam.getParams().containsKey("uuid")
                    ||uriParam.getParams().get("uuid")==null){
                NameValuePair nameValuePair=new BasicNameValuePair("uuid",stateCountUtil.generaterId());
                nameValuePairs.add(nameValuePair);
            }
            try {
                uri=new URIBuilder().setScheme("http")
                        .setHost(serviceNode.getIp())
                        .setPort(serviceNode.getPort())
                        .setPath(serviceNode.getName())
                        .addParameters(nameValuePairs)
                        .build();
            } catch (URISyntaxException e) {
                e.printStackTrace();
            }

        }
        return uri;
    }

    /**
     * 判断uri请求参数里面有没有uui的，没有就生成一个
     * @param uriParam
     */
    private void generateUuid(UriParam uriParam){
        if(uriParam!=null&&!uriParam.getParams().containsKey("uuid")
                &&uriParam.getParams().get("uuid")==null){
            uriParam.getParams().put("uuid",stateCountUtil.generaterId());
        }
    }

    /**
     * 解析uri 封装
     * @param uri
     * @return
     */
    private  UriParam getUri(String uri){
        String name=null;
        String[] nameSplit=null;
        UriParam uriParam=new UriParam();
        if(uri!=null&&uri.length()>0){
            nameSplit=uri.split("[?]");
            if(nameSplit.length==1){
                uriParam.setUri(nameSplit[0]);
            }else if(nameSplit.length==2){
                uriParam.setUri(nameSplit[0]);
                uriParam.setParams(decodeUrlParam(nameSplit[1]));
            }
        }
        return uriParam;
    }

    private  Map<String,String> decodeUrlParam(String paramUri){
        Map<String,String> params=new HashMap<>();
        String[] kvs= paramUri.split("[&]");
        for(String param:kvs){
            String[] kv=param.split("[=]");
            if(kv.length==2){
                params.put(kv[0],kv[1]);
            }
        }
        return params;
    }

    /**
     * 验证请求当中的type，没有type字段或者type字段的值不合法都返回false
     * 当type是atom时，同时检测location字段是否存在，不在返回false
     * @param uriParam
     * @return
     */
    private boolean checkType(UriParam uriParam){
        if(uriParam.getParams().containsKey("type")){
            String type=uriParam.getParams().get("type");
            if(type.equals("atom")){
                if(uriParam.getParams().containsKey("location")){
                    return true;
                }else
                    return false;
            }
            if(type.equals("combination"))
                return true;
        }
        return false;
    }


    /**
     * 服务路由
     * @param serviceNodes  可用的服务节点
     * @return
     */
    /*private String route(List<ServiceNode> serviceNodes){
        StringBuilder sb=new StringBuilder();
        ServiceNode node=null;
        String ip=null;
        if(serviceNodes==null||serviceNodes.size()==0){
            return null;
        }
        Random random=new Random();
        if(route.equals("random")){
            int index=random.nextInt(serviceNodes.size());
            node=serviceNodes.get(index);
            sb.append(node.getIp()).append(":").append(node.getPort())
                    .append("/").append(node.getName());
            ip=sb.toString();
        }else if(route.equals("weight")){

        }
        logger.info("route to ip :"+ip);
        return ip;
    }*/

    private ServiceNode route(List<ServiceNode> serviceNodes){
        ServiceNode node=null;
        if(serviceNodes==null||serviceNodes.size()==0){
            return null;
        }
        Random random=new Random();
        if(route.equals("random")){
            int index=random.nextInt(serviceNodes.size());
            node=serviceNodes.get(index);
        }else if(route.equals("weight")){
            //TODO 权重路由
        }
        return node;
    }
}
